#ifndef __NET_PEER_H__
#define __NET_PEER_H__

#include <memory>
#include <mutex>
#include <atomic>


#include "ThreadSafeQueue.hpp"
#include <unordered_map>
#include "NetConstants.h"

namespace LiteNetLib
{
    enum class ConnectionState
    {
        Outgoing            = 1 << 1,
        Connected           = 1 << 2,
        ShutdownRequested   = 1 << 3,
        Disconnected        = 1 << 4,
        Any = Outgoing | Connected | ShutdownRequested
    };

    enum class ConnectRequestResult
    {
        None,
        P2PLose,
        Reconnection,
        NewConnection
    };

    enum class DisconnectResult
    {
        None,
        Reject,
        Disconnect
    };

    enum class ShutdownResult
    {
        None,
        Success,
        WasConnected
    };

    class NetManager;
    class BaseChannel;
    class NetPacket;
    class NetConnectRequestPacket;

    class IncomingFragments
    {
    public:
        std::vector<NetPacket *> fragments;
        int32_t receivedCount;
        int32_t totalSize;
        uint16_t channelID;
        IncomingFragments(int32_t size);
        ~IncomingFragments();
        void clear();
    };

    class NetPeer;
    typedef std::shared_ptr<NetPeer> NetPeerPtr;


    class NetPeer : public std::enable_shared_from_this<NetPeer>
    {
    private:
        NetManager *m_netManager;
        net_endpoint m_endpoint;
        std::string m_address;

        // Ping and RTT
        int32_t m_rtt = 0;
        int32_t m_avgRtt = 0;
        int32_t m_rttCount = 0;
        double m_resendDelay = 27.0;
        int32_t m_pingSendTime = 0;
        int32_t m_rttResetTime = 0;
        // 自上次收到包后经过的时间
        std::atomic<uint32_t> m_timeSinceLastPacket;

        // Common
        std::mutex m_shutdownMutex;

        // MTU
        int32_t m_mtu;
        int32_t m_mtuIdx;
        bool m_finishMtu;
        int32_t m_mtuCheckTime;
        int32_t m_mtuCheckAttempts;
        std::mutex m_mtuMutex;

        // Channels
        std::mutex m_createChannelMutex;
        std::vector<BaseChannel *> m_channels;
        ThreadSafeQueue<BaseChannel *> m_channelSendQueue;
        ThreadSafeQueue<NetPacket *> m_unreliableChannel;

        // Fragment
        std::atomic<uint16_t> m_fragmentID;
        std::unordered_map<uint16_t, IncomingFragments*> m_holdeFragments;
        std::unordered_map<uint16_t, uint16_t> m_deliveredFragments;

        // Merging
        NetPacket *m_mergePacket;
        int32_t m_mergePos;
        int32_t m_mergeCount;

        // Connection
        ConnectionState m_connectionState;
        uint8_t m_connectNum;
        int64_t m_connectTime;

        NetPacket *m_shutdownPacket = nullptr;
        const int32_t shutdownDelay = 300;
        int32_t m_shutdownTime = 0;


        NetPacket *m_pingPacket = nullptr;
        NetPacket *m_pongPacket = nullptr;
        NetPacket *m_connectAcceptPacket = nullptr;

    private:
        size_t m_hashCode;


    private:
        void setMtu(uint8_t mtuIdx);
        void overrideMtu(int32_t value);

        BaseChannel *createChannel(uint8_t idx);

        void processMtuData(char *theData, uint32_t dataSize);

        void sendInternal(const char *theData, uint32_t dataSize, uint8_t channelNumber, DeliveryMethod deliveryMethod);

        void updateMtuLogic(int32_t deltaTime);

    public:
        NetPeer(NetManager *netManager, net_endpoint *endpoint);
        NetPeer(NetManager *netManager, net_endpoint *endpoint, int64_t connectId, uint8_t connectNum);
        virtual ~NetPeer();

        inline double getResendDelay();
        inline int64_t getConnectTime();
        inline uint32_t getTimeSinceLastPacket();

        void setConnectionNum(uint8_t num);
        inline uint8_t getConnectionNum();

        /***********************************************************************************************************
         * @brief 拒绝连接
        ***********************************************************************************************************/ 
        void reject(int64_t connectionId, uint8_t connectionNumber, const char *rejectData, uint32_t dataLength);

        /***********************************************************************************************************
         * @brief 断开客户端的连接
        ***********************************************************************************************************/ 
        void disconnect(const char *disconnectData, uint32_t dataLength);
        void disconnect();

        DisconnectResult processDisconnect(const char *dataBuff, uint32_t dataSize);

        /***********************************************************************************************************
         * @brief 添加可靠通道，后面将发送里面数据
         * @param channel 通道
        ***********************************************************************************************************/ 
        inline void addToReliableChannelSendQueue(BaseChannel *channel);

        ShutdownResult shutdown(const char *shutdownData, uint32_t dataLength, bool force);

        void updateRoundTripTime(int32_t roundTripTime);

        /***********************************************************************************************************
         * @brief 收到可靠的包
         * @param method 传输方式
         * @param packet 数据包
         * @note 传入该函数的 数据包 不可delete或回收处理，函数内部已经做了处理
        ***********************************************************************************************************/ 
        void addReliablePacket(DeliveryMethod method, NetPacket *packet);

        ConnectRequestResult processConnectRequest(NetConnectRequestPacket *connRequest);

        void processData(char *dataBuff, uint32_t dataSize);

        void sendMerged();

        void sendUserPacket(NetPacket *netPacket);

        void update(int32_t deltaTime);
        
        inline const std::string & getAddress() const;
        inline net_endpoint *getEndpoint();

        inline ConnectionState getConnectionState();
        inline size_t getHashCode();
        inline NetManager *getNetManager();

        inline void send(const char *buff, uint32_t buffSize, DeliveryMethod deliveryMethod);
        inline void send(const char *buff, uint32_t buffSize, uint8_t channelNumber, DeliveryMethod deliveryMethod);
    };

    inline double NetPeer::getResendDelay()
    {
        return m_resendDelay;
    }
    inline int64_t NetPeer::getConnectTime()
    {
        return m_connectTime;
    }
    inline uint32_t NetPeer::getTimeSinceLastPacket()
    {
        return m_timeSinceLastPacket;
    }
    inline uint8_t NetPeer::getConnectionNum()
    {
        return m_connectNum;
    }

    inline void NetPeer::addToReliableChannelSendQueue(BaseChannel *channel)
    {
        m_channelSendQueue.push(channel);
    }

    inline const std::string &NetPeer::getAddress() const
    {
        return m_address;
    }
    inline net_endpoint *NetPeer::getEndpoint()
    {
        return &m_endpoint;
    }
    inline ConnectionState NetPeer::getConnectionState()
    {
        return m_connectionState;
    }
    inline size_t NetPeer::getHashCode()
    {
        return m_hashCode;
    }
    inline NetManager *NetPeer::getNetManager()
    {
        return m_netManager;
    }

    inline void NetPeer::send(const char *buff, uint32_t buffSize, DeliveryMethod deliveryMethod)
    {
        sendInternal(buff, buffSize, 0, deliveryMethod);
    }
    inline void NetPeer::send(const char *buff, uint32_t buffSize, uint8_t channelNumber, DeliveryMethod deliveryMethod)
    {
        sendInternal(buff, buffSize, channelNumber, deliveryMethod);
    }

}

#endif
